#include <windows.h>
#include <time.h>
#include <stdlib.h>

#define WIDTH 10
#define HEIGHT 20
#define BLOCK_SIZE 30

typedef struct {
    int x, y;
} Point;

typedef struct {
    Point points[4];
    int color;
} Shape;

Shape shapes[7] = {
    {{{0, 0}, {1, 0}, {0, 1}, {1, 1}}, 1}, // Square
    {{{0, 0}, {1, 0}, {2, 0}, {3, 0}}, 2}, // Line
    {{{0, 0}, {1, 0}, {1, 1}, {2, 1}}, 3}, // S
    {{{0, 1}, {1, 1}, {1, 0}, {2, 0}}, 4}, // Z
    {{{0, 0}, {1, 0}, {2, 0}, {1, 1}}, 5}, // T
    {{{0, 0}, {1, 0}, {2, 0}, {2, 1}}, 6}, // L
    {{{0, 1}, {1, 1}, {2, 1}, {2, 0}}, 7}  // J
};

int board[HEIGHT][WIDTH] = { 0 };

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    WNDCLASSEX wc = { 0 };
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = "TetrisClass";
    RegisterClassEx(&wc);

    HWND hwnd = CreateWindowEx(0, "TetrisClass", "Tetris", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, WIDTH * BLOCK_SIZE, HEIGHT * BLOCK_SIZE,
        NULL, NULL, hInstance, NULL);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

void draw_board(HDC hdc) {
    for (int y = 0; y < HEIGHT; y++) {
        for (int x = 0; x < WIDTH; x++) {
            HBRUSH brush = CreateSolidBrush(board[y][x] ? RGB(255, 0, 0) : RGB(0, 0, 0));
            SelectObject(hdc, brush);
            Rectangle(hdc, x * BLOCK_SIZE, y * BLOCK_SIZE, (x + 1) * BLOCK_SIZE, (y + 1) * BLOCK_SIZE);
            DeleteObject(brush);
        }
    }
}

int is_collision(Shape shape, int dx, int dy) {
    for (int i = 0; i < 4; i++) {
        int nx = shape.points[i].x + dx;
        int ny = shape.points[i].y + dy;
        if (nx < 0 || nx >= WIDTH || ny < 0 || ny >= HEIGHT || board[ny][nx]) {
            return 1;
        }
    }
    return 0;
}

void merge_shape(Shape shape) {
    for (int i = 0; i < 4; i++) {
        board[shape.points[i].y][shape.points[i].x] = shape.color;
    }
}

void remove_full_lines() {
    for (int y = 0; y < HEIGHT; y++) {
        int full = 1;
        for (int x = 0; x < WIDTH; x++) {
            if (!board[y][x]) {
                full = 0;
                break;
            }
        }
        if (full) {
            for (int ny = y; ny > 0; ny--) {
                for (int x = 0; x < WIDTH; x++) {
                    board[ny][x] = board[ny - 1][x];
                }
            }
            for (int x = 0; x < WIDTH; x++) {
                board[0][x] = 0;
            }
        }
    }
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    static Shape current_shape;
    static int shape_x, shape_y;
    static int game_over = 0;

    switch (msg) {
    case WM_CREATE:
        srand(time(NULL));
        current_shape = shapes[rand() % 7];
        shape_x = WIDTH / 2 - 2;
        shape_y = 0;
        SetTimer(hwnd, 1, 500, NULL);
        break;

    case WM_TIMER:
        if (!game_over) {
            if (!is_collision(current_shape, 0, 1)) {
                shape_y++;
            }
            else {
                merge_shape(current_shape);
                remove_full_lines();
                current_shape = shapes[rand() % 7];
                shape_x = WIDTH / 2 - 2;
                shape_y = 0;
                if (is_collision(current_shape, 0, 0)) {
                    game_over = 1;
                }
            }
            for (int i = 0; i < 4; i++) {
                current_shape.points[i].x += shape_x;
                current_shape.points[i].y += shape_y;
            }
            InvalidateRect(hwnd, NULL, TRUE);
        }
        break;

    case WM_KEYDOWN:
        if (!game_over) {
            if (wParam == VK_LEFT && !is_collision(current_shape, -1, 0)) {
                shape_x--;
            }
            else if (wParam == VK_RIGHT && !is_collision(current_shape, 1, 0)) {
                shape_x++;
            }
            else if (wParam == VK_DOWN && !is_collision(current_shape, 0, 1)) {
                shape_y++;
            }
            else if (wParam == VK_UP) {
                Shape rotated_shape = current_shape;
                for (int i = 0; i < 4; i++) {
                    int x = rotated_shape.points[i].y;
                    int y = -rotated_shape.points[i].x;
                    rotated_shape.points[i].x = x;
                    rotated_shape.points[i].y = y;
                }
                if (!is_collision(rotated_shape, 0, 0)) {
                    current_shape = rotated_shape;
                }
            }
            for (int i = 0; i < 4; i++) {
                current_shape.points[i].x += shape_x;
                current_shape.points[i].y += shape_y;
            }
            InvalidateRect(hwnd, NULL, TRUE);
        }
        break;

    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        draw_board(hdc);
        EndPaint(hwnd, &ps);
    } break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}